/*
@Time : 2020/3/10 0010 11:35
@Author : visons
*/
package setnx_lock

import (
	"errors"
	"github.com/go-redis/redis"
	"time"
)

type SetNXLock struct {
	client *redis.Client
	//key name
	name  string
	value interface{}
	//key automatic expiration time, default 5 seconds.
	expiry time.Duration
	//fail to acquire lock, number of retries.
	//default is 32, with each attempt taking 50 milliseconds
	retries int
}

func NewSetNXLock(c *redis.Client, key string, expiry time.Duration) *SetNXLock {
	return &SetNXLock{
		client: c,
		name:   key,
		expiry: expiry,
	}
}

func (s *SetNXLock) Lock() error {
	if s.name == "" {
		return errors.New("not set key name")
	}

	if s.client == nil {
		return errors.New("not set redis client")
	}

	if s.expiry == 0 {
		s.expiry = time.Second * 5
	}

	if s.retries == 0 {
		s.retries = 32
	}

	for i := 0; i < s.retries; i++ {
		ok := s.client.SetNX(s.name, s.value, s.expiry).Val()
		if ok {
			return nil
		}
		time.Sleep(time.Millisecond * 50)
	}
	return errors.New("setnx_lock: failed to acquire lock")
}

func (s *SetNXLock) UnLock() error {
	_, err := s.client.Del(s.name).Result()
	if err != nil {
		return err
	}
	return nil
}

func (s *SetNXLock) SetExpiry(expiry time.Duration) {
	s.expiry = expiry
}

func (s *SetNXLock) SetRetries(retries int) {
	s.retries = retries
}
